/*:
 * @plugindesc 輔助像素移動與碰撞
 * @author 小c
 * @version 1.0.0
 * @date 6/5/2020
 *
 * @param Show Virtual Event
 * @text 顯示虛擬事件圖片
 * @type boolean
 * @on 顯示
 * @off 不顯示
 * @desc 顯示虛擬事件圖片
 * @default false
 *
 * @param Virtual Event ID Plus
 * @text 虛擬事件id偏移量
 * @type number
 * @min 0
 * @desc 虛擬事件的id返回值與主事件id的差值
 * 為了避免事件衝突的一個參數
 * @default 1000
 *
 * @help
 * 輔助YEP碰撞和像素移動，放在他們的下面。
 * 必須放在YEP保存事件位置的上面。
 * 插件參數中顯示阻擋虛擬事件圖片原為測試之用，但特殊情況下個別事件可通
 * 過此方法進行復制。
 * 在配置有碰撞牆的事件中執行事件註釋
 * <Show Virtual Hit Event>
 * 在事件序列中插入以上註釋，事件碰撞區將會顯示主事件的副本，副本事件可
 * 正常觸發執行。
 *
 * 插件指令
 * VirtualEventPlayAnimation
 * 執行下一個事件動畫時，附屬虛擬事件同時執行該動畫。
 * 該插件指令只對接下來的一個事件動畫生效。
 */

var Imported = Imported || {};
Imported.CP_X_AnalogMove = true;

var CP = CP || {};
CP.AnalogMoveHitManager = CP.AnalogMoveHitManager || {};
var params = PluginManager.parameters("CP_X_AnalogMove");

CP.AnalogMoveHitManager.SHOW_VIRTUAL_EVENT = eval(params["Show Virtual Event"]) || false;
CP.AnalogMoveHitManager.VIRTUAL_EVENT_ID_PLUS = Number(params["Virtual Event ID Plus"]) || 1000;

//--------------------------------
// Game_CharacterBase
// --------------------------------
//是否為碰撞虛擬事件
Game_CharacterBase.prototype.isVirtualEvent = function(){
	return false;
};

Game_CharacterBase.prototype.isBigHitEvent = function(){
	return false;
};

Game_CharacterBase.prototype.hitEvents = function(){
	return [];
};

CP.AnalogMoveHitManager.CHARACTER_REQUEST_ANIMATION = Game_CharacterBase.prototype.requestAnimation;
Game_CharacterBase.prototype.requestAnimation = function(animationId){
    CP.AnalogMoveHitManager.CHARACTER_REQUEST_ANIMATION.call(this, animationId);

    if($gameSystem.isVirtualEventPlayAnimation() && this.isBigHitEvent()){
    	this.hitEvents().forEach(function(event){
    		CP.AnalogMoveHitManager.CHARACTER_REQUEST_ANIMATION.call(event, animationId);
    	});

    	$gameSystem.setVirtualEventPlayAnimation(false);
    }
};

//--------------------------------
// Game_Event
// --------------------------------
CP.AnalogMoveHitManager.INIT_EVENT = Game_Event.prototype.initialize;
Game_Event.prototype.initialize = function(mapId, eventId){
    this._showVirtualEvent = CP.AnalogMoveHitManager.SHOW_VIRTUAL_EVENT;//虛擬事件顯示

    CP.AnalogMoveHitManager.INIT_EVENT.call(this, mapId, eventId);

    this._hitEvents = [];//碰撞事件組
};

Game_Event.prototype.clearHitEvents = function(){
	if(!this.isBigHitEvent())  return;

	this._hitEvents.splice(0, this._hitEvents.length);
	$gameMap.startRemoveVirtualEvent();
};

CP.AnalogMoveHitManager.SETUP_EVENT_PAGE = Game_Event.prototype.setupPage;
Game_Event.prototype.setupPage = function(){
	CP.AnalogMoveHitManager.SETUP_EVENT_PAGE.call(this);

	if(this.isBigHitEvent())
		this.setupVirtualEventVisible();
};

Game_Event.prototype.setupVirtualEventVisible = function(){
	this._showVirtualEvent = CP.AnalogMoveHitManager.SHOW_VIRTUAL_EVENT;

	if(!this.page()) return;

	var reg = /<Show Virtual Hit Event>/;
	this.list().forEach(function(ev){
		if([108, 408].contains(ev.code)){
			var note = ev.parameters[0];
			if(reg.exec(note))
				this._showVirtualEvent = true;
		}
	}, this);
};

Game_Event.prototype.isVirtualEventsShown = function(){
	return this._showVirtualEvent;
};

Game_Event.prototype.addHitEvent = function(event){
	if(!!event){
		this._hitEvents.push(event);
		$gameMap.startAddVirtualEvent();
	}
};

Game_Event.prototype.mapId = function(){
	return this._mapId;
};

//是否區域碰撞
Game_Event.prototype.isBigHitEvent = function(){
	return !!this._addedHitboxLeft || !!this._addedHitboxRight || !!this._addedHitboxUp || !!this._addedHitboxDown;
};

//碰撞區域計算，得到邊界座標
Game_Event.prototype.hitBoxArea = function(){
	var area = {left: this.x, right: this.x, up: this.y, down: this.y};
	var sType = $dataMap.scrollType;

	area.left -= this._addedHitboxLeft;
	area.right += this._addedHitboxRight;
	area.up -= this._addedHitboxUp;
	area.down += this._addedHitboxDown;

	if(area.left < 0){
		if(sType <= 1) //橫向不循環
			area.left = 0;
		else
			area.left += $dataMap.width;
	}

	if(area.right >= $dataMap.width){
		if(sType <= 1) //橫向不循環
			area.right = $dataMap.width;
		else
			area.right -= $dataMap.width;
	}

	if(area.up < 0){
		if(sType % 2 === 0) //縱向不循環
			area.up = 0;
		else
			area.up += $dataMap.height;
	}

	if(area.down >= $dataMap.height){
		if(sType % 2 === 0) //縱向不循環
			area.down = $dataMap.height;
		else
			area.down -= $dataMap.height;
	}

	return area;
};

//計算所有碰撞點
Game_Event.prototype.hitPoints = function(){
	var area = this.hitBoxArea();
	var points = new Array();
	var left = area.left;
	var right = area.right;
	var up = area.up;
	var down = area.down;

	if(left > right)
		right += $dataMap.width;

	if(up > down)
		down += $dataMap.height;

	for(var x = left; x <= right; x++){
		for(var y = up; y <= down; y++){
			var point = {};
			point.x = x % $dataMap.width;
			point.y = y % $dataMap.height;
			if(this.x !== point.x || this.y !== point.y)
				points.push(point);
		}
	}

	return points;
};

Game_Event.prototype.hitEvents = function(){
	if(!this._hitEvents)
		return [];

	return this._hitEvents;
};

CP.AnalogMoveHitManager.EVENT_LOCATE = Game_Event.prototype.locate;
Game_Event.prototype.locate = function(x, y){
	if(!!this._hitEvents){
		this._hitEvents.forEach(function(event){
			var rp = event.relativePoint();
			var hx = x + rp.x;
			var hy = y + rp.y;

			if(hx < 0)
				hx += $dataMap.width;
			else if(hx >= $dataMap.width)
				hx -= $dataMap.width;

			if(hy < 0)
				hy += $dataMap.height;
			else if(hy >= $dataMap.height)
				hy -= $dataMap.height;

			event.locate(hx, hy);
		});
	}

	CP.AnalogMoveHitManager.EVENT_LOCATE.call(this, x, y);
};

Game_Event.prototype.moveStraight = function(d){
	Game_Character.prototype.moveStraight.call(this, d);

	if(!!this._hitEvents){
		this._hitEvents.forEach(function(event){
			Game_Character.prototype.moveStraight.call(event, d);
		});
	}
};

CP.AnalogMoveHitManager.REFRESH_EVENT = Game_Event.prototype.refresh;
Game_Event.prototype.refresh = function(){
	this.clearHitEvents();
	CP.AnalogMoveHitManager.REFRESH_EVENT.call(this);
};

Game_Event.prototype.moveDiagonally = function(horz, vert) {
	Game_Character.prototype.moveDiagonally.call(this, horz, vert);

	if(!!this._hitEvents){
		this._hitEvents.forEach(function(event){
			Game_Character.prototype.moveDiagonally.call(event, horz, vert);
		});
	}
};

Game_Event.prototype.jump = function(xPlus, yPlus){
	Game_Character.prototype.jump.call(this, xPlus, yPlus);

	if(!!this._hitEvents){
		this._hitEvents.forEach(function(event){
			Game_Character.prototype.jump.call(event, xPlus, yPlus);
		});
	}
};

//--------------------------------
// Game_VirtualHitEvent 虛擬碰撞事件
// --------------------------------
function Game_VirtualHitEvent(){
	this.initialize.apply(this, arguments);
};

Game_VirtualHitEvent.prototype = Object.create(Game_Event.prototype);
Game_VirtualHitEvent.prototype.constructor = Game_VirtualHitEvent;

Game_VirtualHitEvent.prototype.initialize = function(baseEvent, x, y){
	Game_Event.prototype.initialize.call(this, baseEvent.mapId(), baseEvent.eventId());

	this.locate(x, y);
	this._hitEvents = null;
	this._baseEvent = baseEvent;
};

Game_VirtualHitEvent.prototype.isBigHitEvent = function(){
	return false;
};

Game_VirtualHitEvent.prototype.isVirtualEvent = function(){
	return true;
};

Game_VirtualHitEvent.prototype.eventId = function(){
	var id = !!this._baseEvent ? this._baseEvent.eventId() : this._eventId;

	return id + CP.AnalogMoveHitManager.VIRTUAL_EVENT_ID_PLUS;
};

Game_VirtualHitEvent.prototype.event = function(){
	return !!this._baseEvent ? this._baseEvent.event() : Game_Event.prototype.event.call(this);
};

Game_VirtualHitEvent.prototype.resetHitbox = function(){
	this._addedHitboxUp = 0;
    this._addedHitboxLeft = 0;
    this._addedHitboxRight = 0;
    this._addedHitboxDown = 0;
};

Game_VirtualHitEvent.prototype.setupPage = function(){
	Game_Event.prototype.setupPage.call(this);

	this.resetHitbox();
};

Game_VirtualHitEvent.prototype.relativePoint = function(){
	return {x: this.x - this._baseEvent.x, y: this.y - this._baseEvent.y};
};

Game_VirtualHitEvent.prototype.locate = function(x, y){
	CP.AnalogMoveHitManager.EVENT_LOCATE.call(this, x, y);
};

Game_VirtualHitEvent.prototype.isThrough = function(){
	return !!this._baseEvent && this._baseEvent.isThrough();
};

Game_VirtualHitEvent.prototype.start = function(){
	this._baseEvent.start();
};

Game_VirtualHitEvent.prototype.moveSpeed = function() {
    return this._baseEvent.moveSpeed();
};

Game_VirtualHitEvent.prototype.moveFrequency = function() {
    return this._baseEvent.moveFrequency();
};

Game_VirtualHitEvent.prototype.opacity = function() {
    return !!this._baseEvent && this.isVisible() ? this._baseEvent.opacity() : 0;
};

Game_VirtualHitEvent.prototype.blendMode = function(){
    return !!this._baseEvent ? this._baseEvent.blendMode() : 0;
};

Game_VirtualHitEvent.prototype.isSaveLocation = function() {
	return false;
};

Game_VirtualHitEvent.prototype.isVisible = function(){
	if(!this._baseEvent)
		return false;

	return this._baseEvent.isVirtualEventsShown();
};

Game_VirtualHitEvent.prototype.mouseCursor = function(){
	return this._baseEvent.mouseCursor();
};

Game_VirtualHitEvent.prototype.isMouseCursorChangeEnabled = function(){
	return this._baseEvent.isMouseCursorChangeEnabled();
};

//--------------------------------
// Game_Map
// --------------------------------
CP.AnalogMoveHitManager.INIT_MAP = Game_Map.prototype.initialize
Game_Map.prototype.initialize = function(){
	CP.AnalogMoveHitManager.INIT_MAP.call(this);

	this.resetVirtualEventFlag();
};

Game_Map.prototype.resetVirtualEventFlag = function(){
	this._addingVirtualEvent = false;
	this._removingVirtualEvent = false;
};

Game_Map.prototype.startAddVirtualEvent = function(){
	this._addingVirtualEvent = true;
};

Game_Map.prototype.finishAddVirtualEvent = function(){
	this._addingVirtualEvent = false;
};

Game_Map.prototype.startRemoveVirtualEvent = function(){
	this._removingVirtualEvent = true;
};

Game_Map.prototype.finishRemoveVirtualEvent = function(){
	this._removingVirtualEvent = false;
};

Game_Map.prototype.isAddingVirtualEvent = function(){
	return this._addingVirtualEvent;
};

Game_Map.prototype.isRemovingVirtualEvent = function(){
	return this._removingVirtualEvent;
};

CP.AnalogMoveHitManager.SETUP_EVENTS = Game_Map.prototype.setupEvents;
Game_Map.prototype.setupEvents = function(){
	CP.AnalogMoveHitManager.SETUP_EVENTS.call(this);
	
	this.setupHitEvents();
};

Game_Map.prototype.setupHitEvents = function(){
	var events = this._events.map(function(event){
		return event;
	});

	events.forEach(function(event){
		if(event && event.isBigHitEvent())
			this.addHitEvents(event);
	}, this);
};

Game_Map.prototype.addHitEvents = function(baseEvent){
	baseEvent.hitPoints().forEach(function(point){
		var virtualEvent = new Game_VirtualHitEvent(baseEvent, point.x, point.y);
		baseEvent.addHitEvent(virtualEvent);
		// this._events.push(virtualEvent);
	}, this);
};

CP.AnalogMoveHitManager.REFRESH_MAP = Game_Map.prototype.refresh;
Game_Map.prototype.refresh = function(){
    CP.AnalogMoveHitManager.REFRESH_MAP.call(this);

    this.clearVirtualEvents();
	this.setupHitEvents();
};

Game_Map.prototype.clearVirtualEvents = function(){
	for(var i = 0; i < this._events.length; i++){
		var event = this._events[i];
		if(event)
			event.clearHitEvents();
	}
};

CP.AnalogMoveHitManager.MAP_EVENTS = Game_Map.prototype.events;
Game_Map.prototype.events = function(){
	return CP.AnalogMoveHitManager.MAP_EVENTS.call(this).concat(this.hitEvents());
};

Game_Map.prototype.hitEvents = function(){
	var virtualEvents = new Array();

	this._events.forEach(function(event){
		if(!!event)
			virtualEvents = virtualEvents.concat(event.hitEvents());
	});

	return virtualEvents;
};

//--------------------------------
// Game_System
// --------------------------------
CP.AnalogMoveHitManager.INIT_SYS = Game_System.prototype.initialize;
Game_System.prototype.initialize = function(){
	CP.AnalogMoveHitManager.INIT_SYS.call(this);

	this.setVirtualEventPlayAnimation(false);
};

Game_System.prototype.isVirtualEventPlayAnimation = function(){
	return !!this._virtualEventPlayAnimation;
};

Game_System.prototype.setVirtualEventPlayAnimation = function(value){
	this._virtualEventPlayAnimation = value;
};

//--------------------------------
// Game_Interpreter
// --------------------------------
CP.AnalogMoveHitManager.PLUGIN_COMMAND = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args){
	CP.AnalogMoveHitManager.PLUGIN_COMMAND.call(this, command, args);

	if(command == 'VirtualEventPlayAnimation')
		$gameSystem.setVirtualEventPlayAnimation(true);
};

//--------------------------------
// Sprite_Character
// --------------------------------
Sprite_Character.prototype.character = function() {
    return this._character;
};

Sprite_Character.prototype.isVirtualEventCharacter = function(){
	return this._character.isVirtualEvent();
};

//--------------------------------
// Spriteset_Map
// --------------------------------
Spriteset_Map.prototype.removeCharacter = function(characterSprite){
	if(!!characterSprite){
		for(var i = 0; i < this._characterSprites.length; i++){
			if(characterSprite === this._characterSprites[i])
				break;
		}
		if(i < this._characterSprites.length)
			this._characterSprites.splice(i, 1);

		this._tilemap.removeChild(characterSprite);
	}
};

Spriteset_Map.prototype.addCharacter = function(characterSprite){
	this._characterSprites.push(characterSprite);
	this._tilemap.addChild(characterSprite);
};

Spriteset_Map.prototype.getCharacterSprite = function(character){
	for(var i = 0; i < this._characterSprites.length; i++){
		if(this._characterSprites[i].character() === character)
			return this._characterSprites[i];
	}

	return null;
};

Spriteset_Map.prototype.getVirtualCharacterObject = function(sprite){
	var events = $gameMap.events().filter(function(event){
		return !!event && event.isVirtualEvent();
	});

	for(var i = 0; i < events.length; i++){
		if(events[i] === sprite.character())
			return events[i];
	}

	return null;
};

Spriteset_Map.prototype.updateAddHitEventSprites = function(){
	$gameMap.hitEvents().forEach(function(event){
		var sprite = this.getCharacterSprite(event);

		if(!sprite){
			sprite = new Sprite_Character(event);
			this.addCharacter(sprite);
		}
	}, this);

	$gameMap.finishAddVirtualEvent();
};

Spriteset_Map.prototype.updateClearHitEvent = function(){
	this._characterSprites.filter(function(sprite){
		return sprite.isVirtualEventCharacter();
	}).forEach(function(sprite){
		var event = this.getVirtualCharacterObject(sprite);

		if(!event)
			this.removeCharacter(sprite);
	}, this);

	$gameMap.finishRemoveVirtualEvent();
};

CP.AnalogMoveHitManager.UPDATE_SPRITESET_MAP = Spriteset_Map.prototype.update;
Spriteset_Map.prototype.update = function(){
	CP.AnalogMoveHitManager.UPDATE_SPRITESET_MAP.call(this);

	if($gameMap.isAddingVirtualEvent())
		this.updateAddHitEventSprites();
	if($gameMap.isRemovingVirtualEvent())
		this.updateClearHitEvent();
};